using System.Collections.Generic;
using System.Reflection;

namespace EntityAop.Interceptors.Support
{
    public class EntityMetadata
    {
        public class Element
        {
            public class ElementValue
            {
                public string Value { get; set; }
                public bool IsAnotherInstanceDependent { get; set; }

                public ElementValue(string value, bool isAnotherInstanceDependent)
                {
                    Value = value;
                    IsAnotherInstanceDependent = isAnotherInstanceDependent;
                }
            }

            protected List<ElementValue> Dependencies { get; set; }
            public Element()
            {
                Dependencies = new List<ElementValue>();
            }

            public void AddDependency(string value, bool isAnotherInstanceDependent)
            {
                Dependencies.Add(new ElementValue(value, isAnotherInstanceDependent));
            }

            public void AddDependency(List<string> values, bool isAnotherInstanceDependent )
            {
                foreach (var value in values)
                {
                    AddDependency(value, isAnotherInstanceDependent);
                }
            }

            public IEnumerable<ElementValue> GetDependencies()
            {
                return Dependencies;
            }
        }

        protected Dictionary<string, Element> DependsOn { get; set; }
        protected Dictionary<string, Element> Dependencies { get; set; }

        public bool IsInitialized { get; set; }

        public EntityMetadata()
        {
            DependsOn = new Dictionary<string, Element>();
            Dependencies = new Dictionary<string, Element>();
        }

        public void AddDependsOnCall(PropertyInfo dependantPropertyInfo, string dependsOn)
        {
            if (dependsOn.Contains("."))
            {
                var splitted = dependsOn.Split('.');
                var memberDepends = new
                                        {
                                            MemberName = splitted[0],
                                            MembersProperty = splitted[1]
                                        };

                var actualType = dependantPropertyInfo.ReflectedType;
                var memberType = actualType.GetProperty(memberDepends.MemberName).PropertyType;

                EntityMetadataHelper.GetEntityMetadata(memberType)
                    .AddDependenciesOfCall(memberDepends.MembersProperty, string.Format("{0}|{1}", actualType.FullName, dependantPropertyInfo.Name), true);
            }

            AddDependency(DependsOn, dependantPropertyInfo.Name, dependsOn);
            AddDependency(Dependencies, dependsOn, dependantPropertyInfo.Name);
        }

        public void Init(EntityMetadata entityMetadata)
        {
            foreach (var element in entityMetadata.GetDependsOn())
            {
                foreach (var dependency in element.Value.GetDependencies())
                {
                    AddDependenciesOfCall(element.Key, dependency.Value, dependency.IsAnotherInstanceDependent );
                }
            }

            IsInitialized = true;
        }

        public void AddDependsOnCall(PropertyInfo dependantPropertyInfo, string[] dependsOn)
        {
            foreach (var dependency in dependsOn)
            {
                AddDependsOnCall(dependantPropertyInfo, dependency);
            }
        }

        public void AddDependenciesOfCall(string dependant, string dependency, bool isAnotherInstanceDependent)
        {
            AddDependency(DependsOn, dependency, dependant, isAnotherInstanceDependent);
            AddDependency(Dependencies, dependant, dependency, isAnotherInstanceDependent);
        }
        
        public void AddDependenciesOfCall(string dependant, string dependency)
        {
            AddDependenciesOfCall(dependant, dependency, false);
        }

        public void AddDependenciesOfCall(string dependant, string[] dependencies)
        {
            foreach (var dependency in dependencies)
            {
                AddDependenciesOfCall(dependant, dependency);
            }
        }

        private static void AddDependency(Dictionary<string, Element> dependencies, string key, string value)
        {
           AddDependency(dependencies, key, value, false);
        }

        private static void AddDependency(Dictionary<string, Element> dependencies, string key, string value, bool isAnotherInstanceDependent)
        {
            if (!dependencies.ContainsKey(key))
            {
                dependencies.Add(key, new Element());
            }

            dependencies[key].AddDependency(value, isAnotherInstanceDependent);
        }

        public IEnumerable<Element.ElementValue> GetDependsOn(string dependant)
        {
            return GetDependency(DependsOn, dependant).GetDependencies();
        }

        public IEnumerable<Element.ElementValue> GetDependencies(string dependsOn)
        {
            if (!Dependencies.ContainsKey(dependsOn))
            {
                return null;
            }

            return GetDependencyElement(dependsOn).GetDependencies();
        }

        public Element GetDependencyElement(string dependsOn)
        {
            return GetDependency(Dependencies, dependsOn);
        }

        public Dictionary<string, Element> GetDependsOn()
        {
            return DependsOn;
        }

        private Element GetDependency(Dictionary<string, Element> dependencies, string key)
        {
            if (dependencies.ContainsKey(key))
            {
                return dependencies[key];
            }

            return new Element();
        }
    }
}